import { uuid, isStringT, isNst, isInt, sleep, isNumber } from "@mac-xiang/method";
// import * as io from "weapp.socket.io";
const io = require("weapp.socket.io");

declare function parseInt(s: string | number, radix?: number): number;
type ns = number | string;
interface oa { [porp: string]: any; };
interface fun { (param: any): any; }
interface of { [porp: string]: fun; };

export interface websocket {
  on: { (event: string, callback: fun): websocket; };
  emit: { (method: string, data: any): void; };
}
export interface MSG {
  time: number; // 当前时间 一般取值 new Date().getTime()
  type: string; // 当前消息类型
  source: ns; // 发送人的id
  content: string; // 发送内容
  con: string; // 发送的会话id
  users?: ns[];
  result?: any;
  callback?: string;
  // [prop: string]: any;
}
export function getNSa(p: any): ns[] | undefined {
  if (Array.isArray(p)) {
    const r: ns[] = [];
    p.forEach(a => {
      if (isInt(a) || isStringT(a)) {
        r.push(a);
      }
    });
    if (r.length) return r;
  }
}
export function toMSG(p: any): MSG | undefined {
  if (
    p &&
    isStringT(p.type) &&
    isNst(p.source)
  ) {
    const r: MSG = {
      time: parseInt(Date.now() / 1000),
      type: p.type,
      source: p.source,
      content: typeof p.content == "string" ? p.content : "",
      con: p.con,
      result: p.result,
      callback: p.callback
    };
    const u = getNSa(p.users);
    if (u) r.users = u;
    return r;
  }
};
export interface createTmkSocket {
  address: string; // 服务器地址
  token: string; // 服务器认证信息
  reconnect?: number; // 重新连接尝试次数; 小于0 永远尝试连接;0|undefind 不重新连接.
  reTime?: number; // 重新连接间隔;最小值5000毫秒
  callback?: fun; // 连接成功后回调
  eventFuns?: of; // 需要覆盖监听的事件函数集合
}

export const history: oa = {}, clients: ns[] = [],
  callbacks: {
    [porp: string]: {
      timeout: number;
      callback: fun;
      param?: any;
    };
  } = {},
  events: oa = {}, ioEvent = ["recv", "join"];

function clearCallbacks() {
  const time = Date.now();
  Object.keys(callbacks).forEach(k => {
    if (!(callbacks[k].timeout > time)) {
      try {
        callbacks[k].callback({ err: "超时" });
      } catch (error) { }
      delete callbacks[k];
    }
  });
}
setInterval(() => {
  try {
    clearCallbacks();
  } catch (error) { }
}, 2000);
export function callbackEvent(d: MSG, event = "recv") {
  if (d.callback) {
    if (callbacks[d.callback]) {
      callbacks[d.callback].callback(d);
      delete callbacks[d.callback];
    }
  } else if (events[event]) Object.keys(events[event]).map(k => { events[event][k](d); });
}
let socket: websocket;
export class tmkSocket {
  socket: websocket;
  constructor (param: createTmkSocket) {
    this.cs(param);
  }
  private async cs(param: createTmkSocket) {
    if (!param.eventFuns) param.eventFuns = {};
    if (typeof param.reconnect != "number") param.reconnect = 0;
    if (!isNumber(param.reTime) || !param.reTime) param.reTime = 0;
    if (param.reTime > 0 && param.reTime < 5000) param.reTime = 5000;
    let s: any;
    s = await this.createSocket(param.address, param.token, param.eventFuns);
    if (s) {
      this.socket = s;
      socket = s;
    } else {
      await sleep(param.reTime);
      if (param.reconnect < 0 || param.reconnect-- > 0) this.cs(param);
    }
  }
  private createSocket(addr: string, token: string, funs: of) {
    return new Promise<websocket | void>((resolve) => {
      const keys = Object.keys(funs);
      const socket: websocket = io(addr, { query: { token } });
      if (keys.indexOf("method") < 0) socket.on("method", this.method);
      if (keys.indexOf("message") < 0) socket.on("message", this.message);
      if (keys.indexOf("join") < 0) socket.on("join", this.join);
      keys.forEach(k => { socket.on(k, funs[k]); });
      socket.on("error", () => { resolve(); });
      socket.on("connect", () => { resolve(socket); });
    });
  }
  private method(d: MSG) {
    switch (d.type) {
      case "con":
        if (d.result) {
          history[d.con] = d.result;
        }
        break;
      case "join":
        if (d.result && d.con) history[d.con] = d.result;
        break;
      case "login":
        if (d.result) {
          Object.assign(history, d.result.history);
        } else {
          console.log("登录失败", d);
        }
        break;

      default:
        console.log("method", d);
        return;
    }
    callbackEvent(d);
  }
  private message(d: MSG) {
    if (!history[d.con]) history[d.con] = [];
    history[d.con].push(d);
    callbackEvent(d);
  }
  private join(d: MSG) {
    if (d.source && clients.indexOf(d.source) < 0) {
      clients.push(d.source);
    }
    callbackEvent(d, "join");
  }
  send(data: MSG, method: string) {
    return send(data, method);
  }
}
export function send(data: MSG, method: string) {
  return new Promise((resolve) => {
    if (method != "method") method = "message";
    data.callback = uuid;
    callbacks[data.callback] = {
      callback: resolve,
      timeout: +new Date + 10000
    };
    socket.emit(method, data);
  });
}
export default tmkSocket;

// VueComponent
// import Vue from "vue";
// Vue.mixin({
//   created() {
//     const that: any = this;
//     if (that.$options && that.$options.events) {
//       Object.keys(that.$options.events).forEach(key => {
//         let handler = that.$options.events[key];
//         if (typeof handler === 'string') {
//           handler = that[handler];
//         }
//         that[key + '::handler'] = handler.bind(that);
//         that.$root.$on(key, that[key + '::handler']);
//         if (ioEvent.indexOf(key) >= 0) {
//           if (!that.events) that.events = {};
//           that.events[key] = uuid;
//           if (!events[key]) events[key] = {};
//           events[key][that.events[key]] = that[key + '::handler'];
//         }
//       });
//     }
//   },
//   beforeDestroy() {
//     const that: any = this;
//     if (that.$options && that.$options.events) {
//       Object.keys(that.$options.events).forEach(key => {
//         that.$root.$off(key, that[key + '::handler']);
//         if (ioEvent.indexOf(key) >= 0) {
//           Object.keys(events[key]).map(k => {
//             delete events[key][k];
//           });
//         }
//       });
//     }
//   }
// });
